using System;
using System.Collections.Generic;
using ACS.Compiler.Parser;
using ACS.Compiler.Tables.Modes;
using ACS.Compiler.Tree.Impl;
using ACS.Compiler.Util;

namespace ACS.Compiler.Tables
{
    public class ModesTable
    {
        private static ModesTable _instance;
        public static ModesTable Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new ModesTable();
                }
                return _instance;
            }
        }

        private Dictionary<string, StructMode> _table = new Dictionary<string, StructMode>();

        public Dictionary<string, StructMode> Table
        {
            get { return _table; }
        }

        public void AddMode(TypeDef def)
        {
            List<Field> fields = new List<Field>();
            foreach (TypeField field in def.Fields)
            {
                switch (field.Declarer.TypeTag)
                {
                    case TypeDeclarer.Tags.String:
                        fields.Add(new Field(StringMode.Instance, field.FieldName));
                        break;
                    case TypeDeclarer.Tags.Int:
                        fields.Add(new Field(IntMode.Instance, field.FieldName));
                        break;
                    case TypeDeclarer.Tags.Bool:
                        fields.Add(new Field(BoolMode.Instance, field.FieldName));
                        break;
                    case TypeDeclarer.Tags.Structure:
                        StructTypeDeclarer declarer = (StructTypeDeclarer)field.Declarer;
                        StructMode mode = FindMode(declarer.Namespace, declarer.Name);
                        if (declarer.Count > 0)
                        {
                            fields.Add(new Field(new ArrayMode(mode, declarer.Count), field.FieldName));
                        }
                        else
                        {
                            fields.Add(new Field(mode, field.FieldName));
                        }
                        break;
                    case TypeDeclarer.Tags.Void:
                        Logger.Instance.TraceError("Wrong mode of field: void ");
                        break;
                    default:
                        Logger.Instance.TraceError("Wrong mode: unknown");
                        break;
                }
            }
            StructMode newMode = new StructMode(
                FileParser.CurrentFileParser.ModuleName,
                def.Name,
                fields);

            _table.Add(
                CreateName(FileParser.CurrentFileParser.ModuleName, def.Name),
                newMode);

        }

        private static string CreateName(string namespase, string name)
        {
            return namespase + "." + name;
        }

        public Mode FindMode(TypeDeclarer declarer)
        {
            switch (declarer.TypeTag)
            {
                case TypeDeclarer.Tags.String:
                    return StringMode.Instance;
                case TypeDeclarer.Tags.Int:
                    return IntMode.Instance;
                case TypeDeclarer.Tags.Bool:
                    return BoolMode.Instance;
                case TypeDeclarer.Tags.Structure:
                    {
                        StructType1Declarer decl1 = declarer as StructType1Declarer;
                        if (decl1!= null)
                        {
                            if (decl1.Array)
                            {
                                return new Array1Mode(FindMode(decl1.Namespace, decl1.Name));
                            } else
                            {
                                return FindMode(decl1.Namespace, decl1.Name );
                            }
                        }
                        StructTypeDeclarer decl = declarer as StructTypeDeclarer;
                        if (decl!= null)
                        {
                            if (decl.Count > 0)
                            {
                                return new ArrayMode(FindMode(decl.Namespace, decl.Name ), decl.Count);
                            } else
                            {
                                return FindMode(decl.Namespace, decl.Name);
                            }
                        }
                        break;
                    }
                case TypeDeclarer.Tags.Void:
                    return null;
            }
            Logger.Instance.TraceError("Wrong mode decalrer in find mode.");
            return null;
        }
        
        private StructMode FindMode(string typeNamespace, string typeName)
        {
            if (typeNamespace !=null && !FileParser.IsNamespace(typeNamespace))
            {
                Logger.Instance.TraceError("Wrong namespace " + typeNamespace);
            } 
            if (typeNamespace == null)
            {
                typeNamespace = FileParser.CurrentFileParser.ModuleName; 
            }
            StructMode ret;
            string fullName = typeNamespace + "." + typeName;
            _table.TryGetValue(fullName, out ret);
            if (ret == null)
            {
                Logger.Instance.TraceError("Wrong mode... " + fullName);
            }
            return ret;
        }


        /// <summary>
        /// First mode is castable to the second one... m2 = (..)m1;
        /// </summary>
        /// <param name="m1"></param>
        /// <param name="m2"></param>
        /// <returns></returns>
        public static bool Castable(Mode m1, Mode m2)
        {
            return m2.Equals(m1);
        }

        public string TypeToString(Mode mode)
        {
            if (mode == null)
            {
                return "void";
            } else
            {
                return mode.ToDeclareFormat();
            }
        }
    }
}